#include <assert.h>
#include <string.h>

#include "buffer.hpp"

bool Buffer::write (byte_t * ptr, unsigned int len)
{
  if (this->mode == BM_NULL)
    this->mode = BM_BLOCK;

  assert (this->mode == BM_BLOCK);

  BufferNode * node = new BufferNode (ptr, len);
  attach (this->root.prev, node);
  return true;
}

bool Buffer::write (byte_t * ptr, unsigned int len, u_int32_t seq)
{
  if (this->mode == BM_NULL)
    this->mode = BM_STREAM;

  assert (this->mode == BM_STREAM);

  BufferNode * node = new BufferNode (ptr, len, seq);
  for (BufferNode * bp = this->root.next; bp != &this->root; bp = bp->next)
    {
      if (bp->seq < seq)
	{
	  attach (bp, node);
	  return true;
	}
    }

  attach (this->root.prev, node);
  return true;
}



byte_t * Buffer::fetch (unsigned int * len)
{
  unsigned int size;
  if (0 == (size = getValidSize ()))
    return NULL;

  // Allocated memory will not be managed by Buffer class (throw to client)
  byte_t * rp = new byte_t [size];
  unsigned int c = 0;
  BufferNode * bp, * next;
  *len = size;

  if (this->mode == BM_BLOCK)
    {
      for (bp = this->root.next; bp != &(this->root); bp = next)
	{
	  next = bp->next;
	  memcpy (&rp[c], bp->ptr, bp->len);
	  c += bp->len;
	  this->detach (bp);
	  delete bp;
	}
    }
  else if (this->mode == BM_STREAM)
    {
      u_int32_t seq = this->root.next->seq; // head sequence number
      for (bp = this->root.next; bp != &(this->root); bp = next)
	{
	  next = bp->next;

	  if (seq < bp->seq) 
	    break;

	  if (bp->seq + bp->len <= seq) // not needed buffer node
	    {
	      this->detach (bp);
	      delete bp;
	      continue;
	    }

	  unsigned int adjLen = (bp->seq < seq) ? (seq - bp->seq) : 0;
	  unsigned int cpyLen = bp->len - adjLen;
	  memcpy (&rp[c], &bp->ptr[adjLen], cpyLen);
	  c += cpyLen;
	  seq += cpyLen;
	  this->detach (bp);
	  delete bp;
	}
    }
  else
    assert (0);  
  
  assert (size == c);
  return rp;
}

byte_t * Buffer::fetch (unsigned int * len, unsigned int reqLen)
{
  /* to be implemented */
  return NULL;
}

unsigned int Buffer::getValidSize ()
{
  unsigned int len = 0;
  BufferNode * bp;

  if (this->root.next == &(this->root)) // no buffer node
    return 0;

  if (this->mode == BM_BLOCK)
    {
      for (bp = this->root.next; bp != &(this->root); bp = bp->next)
	len += bp->len;
    }
  else if (this->mode == BM_STREAM)
    {
      u_int32_t seq = this->root.next->seq; // head sequence number
      for (bp = this->root.next; bp != &(this->root); bp = bp->next)
	{
	  if (seq < bp->seq) 
	    break;

	  if (bp->seq + bp->len <= seq) // not needed buffer node
	    continue;

	  // Adjusting invalid buffer node length in this loop
	  int vLen = (bp->seq < seq) ? bp->len - (seq - bp->seq) : bp->len;
	  len += vLen;
	  seq += vLen;
	}
    }
  else
    assert (0);  

  return len;
}

void Buffer::attach (BufferNode * prev, BufferNode * node)
{
  assert (prev);
  BufferNode * next = prev->next;
  next->prev = node;
  prev->next = node;
  node->next = next;
  node->prev = prev;
  debug (DEBUG_LINK, "attach: (%p) <- (%p) -> (%p)", prev, node, next);
  return ;
}

void Buffer::detach (BufferNode * node)
{
  BufferNode * next = node->next;
  BufferNode * prev = node->prev;
  debug (DEBUG_LINK, "detach (%p) <- (%p) -> (%p)", prev, node, next);
  next->prev = prev;
  prev->next = next;
  node->prev = node;
  node->next = node;
  return ;
}



void BufferNode::setData (byte_t * p, unsigned int size)
{
  if (this->ptr && this->len < size)
    { 
      delete this->ptr;
      this->ptr = new byte_t [size];
    }
  else  if (this->ptr == NULL)
    this->ptr = new byte_t [size];

  this->len = size;
  memcpy (this->ptr, p, size);
  return ;
}

void BufferNode::setData (byte_t * p, unsigned int size, u_int32_t seq)
{
  this->seq = seq;
  this->setData (p, size);
}
